<?php

class IDAO extends IObject {
	private static $daos = array();
	
	private $props = array();
	
	/**
	 * 数据库驱动
	 *
	 * @var IDbDriver
	 */
	private $driver;
	
	private $table;
	
	private $quotedTable;
	
	private $type;
	
	private $columns = array();
	
	private $pkName;
	
	private $readonly = false;
	
	private $relations = array();
	
	private $validates = array();
	
	/**
	 * Enter description here...
	 *
	 * @var IDbQuery
	 */
	private static $querys = array();
	
	public function __construct($type) {	
		if (!class_exists($type, true)) {
			throw new IActiveRecordException("record class '{$type}' does not exist");
		}
		if (!is_subclass_of($type, "IDbRecord")) {
			throw new IActiveRecordException("'{$type}' must be inherited from IDbRecord");
		}
		
		$this->props = get_class_vars($type);
		
		//类型
		$this->type = $type;
		
		//驱动
		if (isset($this->props["_driver"])) {
			$config = $this->props["_driver"];
		}
		else {
			$config = "default";
		}
		$this->driver = IDb::driverByOpt($config);
		
		//表名
		if (isset($this->props["_table"])) {
			$this->table = $this->props["_table"];
		}
		else {
			$this->table = IVM::singleton()->nameToC($type);
		}
		$this->quotedTable = $this->driver->quoteKeyword($this->table);
		
		
		//columns
		if (isset($this->props["_columns"])) {
			$columns = $this->props["_columns"];
			if (!is_array($columns)) {
				throw new IActiveRecordException("columns of '{$type}' must be an array");
			}
			else {
				$this->columns = $columns;
			}
		}
		else {
			$fields = $this->driver->describeTable($this->table);
			$this->columns = array_keys($fields);
			foreach ($fields as $field) {
				if ($field["primaryKey"]) {
					$this->pkName = $field["column"];
					break;
				}
			}
		}
		
		//pk
		if (isset($this->props["_pk"])) {
			$this->pkName = $this->props["_pk"];
		}
		if ($this->pkName == null) {
			throw new IActiveRecordException("'{$type}' must define a primary key (via \$_pk defination)");
		}
		
		//readonly
		if (isset($this->props["_readonly"])) {
			$this->readonly = $this->props["_readonly"];
		}
		
		//表之间关系
		$vm = IVM::singleton();
		
		//hasOne
		if (isset($this->props["_hasOne"]) && is_array($this->props["_hasOne"])) {
			foreach ($this->props["_hasOne"] as $key => $value) {
				if (!$vm->isValidVariableName($key)) {
					throw new IActiveRecordException("'{$key}' in {$this->type} not a valid attr");
				}
				if (!is_array($value) && strstr($value, ".")) {
					list($target, $column) = explode(".", $value, 2);
					if (!$vm->isValidVariableName($target) || !$vm->isValidVariableName($column)) {
						throw new IActiveRecordException("'{$value}' in {$this->type} not a valid attr");
					}
					$this->relations[$key] = array("type" => IDbRecord::HAS_ONE, "attr" => $key, "fk" =>$this->pkName, "target" => $target, "by" => $column);
				}
				else if (is_array($value) && isset($value["target"]) && isset($value["by"])) {
					$fk = isset($value["pk"])?$value["pk"]:$this->pkName;
					$this->relations[$key] = array("type" => IDbRecord::HAS_ONE, "attr" => $key, "fk" =>$fk, "target" => $value["target"], "by" => $value["by"]);
				}
			}
		}
		
		//hasMany
		if (isset($this->props["_hasMany"]) && is_array($this->props["_hasMany"])) {
			foreach ($this->props["_hasMany"] as $key => $value) {
				if (!$vm->isValidVariableName($key)) {
					throw new IActiveRecordException("'{$key}' in {$this->type} not a valid attr");
				}
				if (!is_array($value) && strstr($value, ".")) {
					list($target, $column) = explode(".", $value, 2);
					if (!$vm->isValidVariableName($target) || !$vm->isValidVariableName($column)) {
						throw new IActiveRecordException("'{$value}' in {$this->type} not a valid attr");
					}
					$this->relations[$key] = array("type" => IDbRecord::HAS_MANY, "attr" => $key, "fk" =>$this->pkName, "target" => $target, "by" => $column);
				}
				else if (is_array($value) && isset($value["target"]) && isset($value["by"])) {
					$fk = isset($value["pk"])?$value["pk"]:$this->pkName;
					$this->relations[$key] = array("type" => IDbRecord::HAS_MANY, "attr" => $key, "fk" =>$fk, "target" => $value["target"], "by" => $value["by"]);
				}
			}
		}

		//many-to-one|one-to-one
		if (isset($this->props["_belongsTo"]) && is_array($this->props["_belongsTo"])) {
			foreach ($this->props["_belongsTo"] as $key => $value) {
				if (!$vm->isValidVariableName($key)) {
					throw new IActiveRecordException("'{$key}' in {$this->type} not a valid attr");
				}
				if (is_array($value) && isset($value["fk"]) && isset($value["target"])) {
					$targetDAO = self::dao($value["target"]);
					$by = isset($value["by"])?$value["by"]:$targetDAO->pkName();
					$this->relations[$key] = array("type" => IDbRecord::BELONGS_TO, "attr" => $key, "fk" =>$value["fk"], "target" => $value["target"], "by" => $by);
				}
			}
		}
		
		//validators
		if (isset($this->props["_validate"]) && is_array($this->props["_validate"])) {
			$this->validates = $this->props["_validate"];
		}
		
		//加入容器中
		self::$daos[$this->type] = $this;
	}
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $type
	 * @return IDAO
	 */
	public static function dao($type) {
		if (!isset(self::$daos[$type])) {
			self::$daos[$type] = new IDAO($type);
		}
		return self::$daos[$type];
	}
	
	/**
	 * Enter description here...
	 * 
	 * 返回影响的行数
	 *
	 * @param IDbRecord $record
	 * @param unknown_type $deep 暂时无用
	 * @return integer
	 */
	public function delete($record, $deep) {
		if ($this->readonly) {
			throw new IActiveRecordException("can't delete readonly record");
		}		
		$count = $this->driver->exec("DELETE FROM " . $this->quotedTable . " WHERE " . $this->driver->quoteKeyword($this->pkName) . "=" . $this->driver->quote($record->pk()));
		return $count;
	}
	
	/**
	 * Enter description here...
	 *
	 * @param IDbRecord $record
	 */
	public function save($record) {
		if (!$record->isSaved()) {
			//构造insert
			$columns = array();
			$values = array();
			foreach ($this->columns as $column) {
				$value = $record->attr($column);
				if (!is_null($value)) {
					$value = $this->driver->quote($value);
					$columns[] = $this->driver->quoteKeyword($column);
					$values[] = $value;
				}
			}
			if (empty($values)) {
				throw new IActiveRecordException("can't insert empty record, need to specify attr values");
				return $record;
			}
			$count = $this->driver->exec("INSERT INTO " . $this->quotedTable . " (" . implode(",", $columns) . ") VALUES (" . implode(",", $values) . ")");
			if ($count > 0) {
				$record->setAttr($this->pkName, $this->driver->lastInsertId());	
			}
			$record->setSaved(true);
			$record->setOriginal($record->attrs());
		}
		else {
			if ($this->readonly) {
				throw new IActiveRecordException("can't update readonly record");
			}
			
			//查找有更新的字段
			$ori = $record->original();
			$attrs = $record->attrs();
			$columns = array();
			foreach ($this->columns as $column) {
				if ($ori[$column] != $attrs[$column]) {
 					$columns[] = $this->driver->quoteKeyword($column) . "=" . $this->driver->quote($record->attr($column));
				}
			}
			if (!empty($columns)) {
				//pk value
				$pkValue = $record->attr($this->pkName);
				
				$this->driver->exec("UPDATE " . $this->quotedTable . " SET " . implode(",", $columns) . " WHERE " . $this->driver->quoteKeyword($this->pkName) . "=" . $this->driver->quote($pkValue));
			}
		}
		
		//关联字段处理
		
		
		return $record;
	}
	
	/**
	 * Enter description here...
	 *
	 * @return IDbDriver
	 */
	public function driver() {
		return $this->driver;
	}
	
	public function type() {
		return $this->type;
	}
	
	public function table() {
		return $this->table;
	}
	
	/**
	 * Enter description here...
	 *
	 * @param array $attrs
	 * @return IDbRecord
	 */
	public function create(array $attrs) {
		$class = $this->type;
		$record = new $class($attrs);
		return $record;
	}
	
	public function readonly() {
		return $this->readonly;
	}
	
	public function pkName() {
		return $this->pkName;
	}
	
	public function columns() {
		return $this->columns;
	}
	
	/**
	 * Enter description here...
	 *
	 * @return IDbQuery
	 */
	public static function query($record) {
		if (!isset(self::$querys[$record])) {
			self::$querys[$record] = new IDbQuery($record);
		}
		else {
			self::$querys[$record]->clear();
		}
		return self::$querys[$record];
	}
	
	public function relations() {
		return $this->relations;
	}
	
	public function relation($attr) {
		if (isset($this->relations[$attr])) {
			return $this->relations[$attr];
		}
		return null;
	}
	
	public function validates() {
		return $this->validates;
	}
	
	public function setDriver($driver) {
		$this->driver = IDb::driverByOpt($driver);
	}
}

?>